package cn.edu.kmust.tdsc.jbmdiff;

public class BMDecompressor {
	
	private static final byte BM_ESC = (byte) 0xFE;
	
	private static final int decode(byte[] in, int i, int width) {
		int result = 0;
		switch (width) {
		case 4:
			result = in[i++] << 24;
			result += (in[i++] << 16);
			result += (in[i++] << 8);
			result += in[i];
			return result;
		case 3:
			result = (in[i++] << 16);
			result += (in[i++] << 8);
			result += in[i];
			return result;
		case 2:
			result = (in[i++] << 8);
			result += in[i];
			return result;
		case 1:
			result = in[i];
			return result;
		default:
			return result;
		}
	}
	
	/**
	 * Decompresses the Input Bytes
	 * @param in Input Bytes
	 * @param out Output Bytes
	 * @return output length
	 */
	public static int decompress(final byte[] in, byte[] out) {
		final int len = in.length;
		int outPos = 0;
		int i = 0;
		int j = 0;
		int startWidth = 0;
		int lengthWidth = 0;
		int start = 0;
		int length = 0;
		
		while(i < len) {
			if (in[i] == BM_ESC) {
				if (in[i + 1] != BM_ESC) {
					// decode
					i++;  // skip BM_ESC
					
					startWidth = ((in[i] & 0xF0) >> 4);
					lengthWidth = (in[i] & 0x0F);
					i++; // skip SIGN
					
					start = decode(in, i, startWidth);
					i += startWidth;  // skip start
					
					length = decode(in, i, lengthWidth);
					i += lengthWidth;

					length += start;
					for (j = start; j < length; j++, outPos++) {
						out[outPos] = out[j];
					}
					continue;
				} else {
					out[i++] = BM_ESC;
				}
			}
			out[outPos++] = in[i++];
		}
		return outPos;
	}
}
